﻿namespace Blaze.Framework
{
    using System;
    using System.Collections;
    using EditorOnly;
    using FileSystems;
    using Logging;
    using LuaExtensions.FileLoaders;
    using UnityEngine;
    using XLua;
    using XLua.LuaDLL;

    /// <summary>
    /// XLua模块实现。
    /// </summary>
    public class XLuaModule : ILuaModule
    {
        /// <summary>
        /// 当模块即将进行重载时触发此事件。
        /// </summary>
        public event Action Reloading;

        /// <summary>
        /// 获取全局使用的Lua环境。
        /// </summary>
        public LuaEnv Env { get; private set; }

        #region ILuaModule Members

        /// <summary>
        /// 执行指定的Lua脚本。
        /// </summary>
        /// <param name="script">脚本</param>
        /// <returns></returns>
        public object[] DoString(string script)
        {
            return Env.DoString(script);
        }

        /// <summary>
        /// 初始化模块。
        /// </summary>
        public void Initialize()
        {
            if (mIsInitialized)
                return;
            Env = CreateEnv();

            mRequire = Env.Global.Get<Func<string, LuaTable>>("require");
            Require("main");
            mIsInitialized = true;

            if (mTickCoroutine == null)
                mTickCoroutine = CoroutineManager.StartNew(tick());
        }

        /// <summary>
        /// 重新载入Lua环境。
        /// </summary>
        public void Reload()
        {
            if (Env == null)
                return;

            CoroutineManager.StartNew(reloadLua());
        }

        /// <summary>
        /// 加载指定路径的Lua脚本。
        /// </summary>
        /// <param name="path">脚本路径</param>
        public LuaTable Require(string path)
        {
            return mRequire(path);
        }

        #endregion

        /// <summary>
        /// 创建模块使用的<see cref="LuaEnv"/>。
        /// </summary>
        /// <returns></returns>
        protected virtual LuaEnv CreateEnv()
        {
            var ret = new LuaEnv();
            var appConfig = AppConfigManager.Get<AppConfig>();
#if UNITY_EDITOR
            if (!appConfig.simulate_runtime)
            {
                ret.AddLoader(new ConfigRedirector(ProjectPath.Get("Configs/ClientData")).ReadFile);
                ret.AddLoader(new FromDirectory(ProjectPath.Get("Lua")).ReadFile);
            }
#endif
            if (appConfig.debug_mode)
                ret.AddLoader(new FromDirectory(PathUtility.GetPersistentDataPath("lua")).ReadFile);
            ret.AddLoader(new FromFileSystem(FileSystem.Default).ReadFile);
            ret.AddBuildin("rapidjson", Lua.LoadRapidJson);
            return ret;
        }

        private IEnumerator reloadLua()
        {
            Reloading?.Invoke();
            mRequire = null;
            yield return null;
            try
            {
                Env.Dispose();
            }
            catch (Exception e)
            {
                mLog.WarnFormat("[XLuaModule]reloading lua exception -> " + e.Message);
                ObjectTranslatorPool.Instance.Remove(Env.L);
            }

            Env = null;
            yield return null;
            mIsInitialized = false;
            Initialize();
        }

        private IEnumerator tick()
        {
            while (true)
            {
                Env?.Tick();
                yield return null;
            }
        }

        private bool mIsInitialized;
        private static readonly Log mLog = LogManager.GetLogger(typeof(XLuaModule));
        private Func<string, LuaTable> mRequire;
        private Coroutine mTickCoroutine;
    }
}